use crate::utils::maybe_deser_owned::MaybeDeserOwned;
use serde::{Deserialize, Serialize, Serializer};
use std::{ops::Deref, sync::Arc};
use tokio_util::bytes::Bytes;

macro_rules! impl_maybe_deser_owned_false {
    ($($typ_name: ident),*) => {
        $(
        unsafe impl MaybeDeserOwned for $typ_name {
            const IS_DESER_OWNED: bool = false;
        }
        )*
    };
}
macro_rules! impl_bytes {
    ($typ_name: ident, $src_typ: ty) => {
        #[derive(Debug, Deserialize)]
        pub struct $typ_name(
            &'static [u8],
            #[allow(dead_code)]
            #[serde(skip)]
            $src_typ,
        );
        impl $typ_name {
            pub fn new(src: $src_typ) -> Self {
                let r: &[u8] = &*src;
                Self(
                    // SAFETY: the outside world can not get this static
                    // reference, and the API of this structure guarantees
                    // that the lifetime of any reference the user can get
                    // is within the lifetime of this structure, thus is
                    // always valid since this structure holds the source
                    unsafe { std::mem::transmute::<&[u8], &[u8]>(r) },
                    src,
                )
            }
        }
        impl Deref for $typ_name {
            type Target = [u8];
            fn deref(&self) -> &Self::Target {
                self.0
            }
        }
        impl Serialize for $typ_name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                serializer.serialize_bytes(self.0)
            }
        }
        impl_maybe_deser_owned_false!($typ_name);
    };
}

impl_bytes!(DeserRefSrcArcBytes, Arc<[u8]>);
impl_bytes!(DeserRefSrcVecBytes, Vec<u8>);
impl_bytes!(DeserRefSrcBoxedBytes, Box<[u8]>);
impl_bytes!(DeserRefSrcBytes, Bytes);

#[derive(Debug, Deserialize)]
pub struct StaticBytes(pub &'static [u8]);
impl Serialize for StaticBytes {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(self.0)
    }
}
impl Deref for StaticBytes {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0
    }
}
impl_maybe_deser_owned_false!(StaticBytes);
